﻿/*
Digraph (library for building, traversal, comparison and saving of directed
graph).

Copyright (C) 2014 Dmitry Merzagitovich Arslanov

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*/

using System;
using System.Collections.Generic;

namespace Digraph
{
    /// <summary>
    /// Ordered pair of digraph vertices represent connection between this two
    /// vertices. Also called directed edge or arrow.
    /// </summary>
    /// <typeparam name="TVertex">Type of digraph vertex data.</typeparam>
    /// <typeparam name="TArc">Type of digraph arc data.</typeparam>
    public sealed class Arc<TVertex, TArc> :
        IEquatable<Arc<TVertex, TArc>>
    {
        /// <summary>
        /// Data of specified type associated with arc.
        /// </summary>
        public TArc Content { get; internal set; }

        /// <summary>
        /// Vertex from which the arc outcomes.
        /// </summary>
        public Vertex<TVertex, TArc> FromVertex { get; internal set; }

        /// <summary>
        /// Vertex to which the arc incomes.
        /// </summary>
        public Vertex<TVertex, TArc> ToVertex { get; internal set; }

        internal Arc(Vertex<TVertex, TArc> fromVertex,
                     Vertex<TVertex, TArc> toVertex,
                     TArc content = default(TArc))
        {
            if (fromVertex == null || toVertex == null)
                throw new ArgumentNullException();
            Content = content;
            FromVertex = fromVertex;
            ToVertex = toVertex;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of
        /// the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// True if the current object is equal to the other parameter;
        /// otherwise, false.
        /// </returns>
        public bool Equals(Arc<TVertex, TArc> other)
        {
            if (other == null) return false;
            return FromVertex == other.FromVertex &&
                   ToVertex == other.ToVertex &&
                   EqualityComparer<TArc>.Default.Equals(Content,
                                                         other.Content);
        }

        /// <summary>
        /// Determines whether the specified object is equal to the current
        /// object.
        /// </summary>
        /// <param name="obj">
        /// The object to compare with the current object.
        /// </param>
        /// <returns>
        /// True if the specified object is equal to the current object;
        /// otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Arc<TVertex, TArc>);
        }

        /// <summary>
        /// Serves as the default hash function.
        /// </summary>
        /// <returns>A hash code for the current object.</returns>
        public override int GetHashCode()
        {
            int hashCode1 = 0;
            if (!EqualityComparer<TArc>.Default.Equals(Content, default(TArc)))
                hashCode1 = Content.GetHashCode();
            int hashCode2 = FromVertex.GetHashCode();
            int hashCode3 = ToVertex.GetHashCode();
            return Tuple.Create(hashCode1, hashCode2, hashCode3).GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified arc instances are considered
        /// equal.
        /// </summary>
        /// <param name="a1">The first arc to compare.</param>
        /// <param name="a2">The second arc to compare.</param>
        /// <returns>
        /// True if the arcs are considered equal; otherwise, false.
        /// If both v1 and v2 are null, the method returns true.
        /// </returns>
        public static bool operator ==(Arc<TVertex, TArc> a1,
                                       Arc<TVertex, TArc> a2)
        {
            return Equals(a1, a2);
        }

        /// <summary>
        /// Determines whether the specified arc instances are considered
        /// not equal.
        /// </summary>
        /// <param name="a1">The first arc to compare.</param>
        /// <param name="a2">The second arc to compare.</param>
        /// <returns>
        /// True if the arcs are considered not equal; otherwise, false.
        /// If both v1 and v2 are null, the method returns false.
        /// </returns>
        public static bool operator !=(Arc<TVertex, TArc> a1,
                                       Arc<TVertex, TArc> a2)
        {
            return !Equals(a1, a2);
        }

        /// <summary>
        /// Returns a string that represents the current arc.
        /// </summary>
        /// <returns>A string that represents the current arc.</returns>
        public override string ToString()
        {
            return string.Format("{0}", Content);
        }
    }
}
